Add support for multiple POI lists to writer code.
authoroliskoli <oliskoli>
Tue, 22 May 2007 21:28:54 +0000 (21:28 +0000)
committeroliskoli <oliskoli>
Tue, 22 May 2007 21:28:54 +0000 (21:28 +0000)
garmin_gpi.c

index 573fb6aa304a67dff456ca5c0f9dfb03d33f6cf7..a0c04e596250cf2c5d9add60bd11a64792c1c871 100644 (file)
@@ -25,7 +25,8 @@
 
        * 2007/05/18: initial release (only a reader)
        * 2007/05/20: added writer code with embedded bitmap
-
+       * 2007/05/22: add support for multiple bounding boxes
+                     (useful / required!) for large waypoints lists
        ToDo:
        
        * Display mode ("Symbol & Name")
@@ -48,6 +49,7 @@
 #undef GPI_DBG
 
 #define DEFAULT_ICON   "Waypoint"
+#define WAYPOINTS_PER_BLOCK    128
 
 static char *opt_cat, *opt_pos, *opt_notes, *opt_hide, *opt_descr;
 
@@ -73,14 +75,24 @@ typedef struct {
        char S8[3];
        char *group;
        char *category;
-} gpi_data_t;
+} reader_data_t;
+
+typedef struct writer_data_s {
+       queue Q;
+       int ct;
+       int sz;
+       bounds bds;
+       struct writer_data_s *top_left;
+       struct writer_data_s *top_right;
+       struct writer_data_s *buttom_left;
+       struct writer_data_s *buttom_right;
+} writer_data_t;
+       
 
 static gbfile *fin, *fout;
 static gbint32 codepage;       /* code-page, i.e. 1252 */
-static gpi_data_t *dt;
-static queue wptq;
-static int all_points_sz;
-static bounds bds;
+static reader_data_t *rdata;
+static writer_data_t *wdata;
 static short_handle short_h;
 
 #ifdef GPI_DBG
@@ -89,29 +101,6 @@ static short_handle short_h;
 # define PP
 #endif
 
-#ifdef GPI_DBG
-static void
-store_bitmap(const char *filename, int sz)
-{
-       gbfile *fout;
-       
-       fout = gbfopen_le(filename, "wb", MYNAME);
-       
-       while (! gbfeof(fin)) {
-               char buff[256];
-               int len;
-               
-               len = sizeof(buff);
-               if (len > sz) len = sz;
-               sz -= len;
-               len = gbfread(buff, 1, len, fin);
-               gbfwrite(buff, 1, len, fout);
-               if (sz <= 0) break;
-       }
-       gbfclose(fout);
-}
-#endif
-
 /*******************************************************************************
 * %%%                             gpi reader                               %%% *
 *******************************************************************************/
@@ -158,20 +147,20 @@ read_header(void)
 
        i = gbfgetint32(fin);
        if (i != 0) i = gbfgetint32(fin);
-       dt->D2 = gbfgetint32(fin);
+       rdata->D2 = gbfgetint32(fin);
 
-       gbfread(&dt->S3, 1, sizeof(dt->S3) - 1, fin);   /* GRMRECnn */
-       if (strncmp(dt->S3, "GRMREC", 6) != 0)
+       gbfread(&rdata->S3, 1, sizeof(rdata->S3) - 1, fin);     /* GRMRECnn */
+       if (strncmp(rdata->S3, "GRMREC", 6) != 0)
                fatal(MYNAME ": No GPI file!\n");
 
        PP;
-       dt->crdate = gbfgetint32(fin);
+       rdata->crdate = gbfgetint32(fin);
 #ifdef GPI_DBG
-       tm = *localtime(&dt->crdate);
+       tm = *localtime(&rdata->crdate);
        tm.tm_year += 20;       /* !!! */
        tm.tm_mday -= 1;        /* !!! */
        strftime(stime, sizeof(stime), "%Y/%m/%d %H:%M:%S", &tm);
-       printf("crdate = %lu (%s)\n", dt->crdate, stime);
+       printf("crdate = %lu (%s)\n", rdata->crdate, stime);
 #endif 
        
        (void) gbfgetint16(fin);        /* 0 */
@@ -182,12 +171,12 @@ read_header(void)
        (void) gbfgetint32(fin);        /* 1 */
        (void) gbfgetint32(fin);        /* 12 */
 
-       gbfread(&dt->POI, 1, sizeof(dt->POI) - 1, fin);
-       if (strcmp(dt->POI, "POI") != 0)
+       gbfread(&rdata->POI, 1, sizeof(rdata->POI) - 1, fin);
+       if (strcmp(rdata->POI, "POI") != 0)
                fatal(MYNAME ": Wrong or unsupported GPI file!\n");
 
        for (i = 0; i < 3; i++) (void)gbfgetc(fin);
-       gbfread(&dt->S8, 1, sizeof(dt->S8) - 1, fin);
+       gbfread(&rdata->S8, 1, sizeof(rdata->S8) - 1, fin);
 
        codepage = gbfgetint32(fin);
 
@@ -312,10 +301,10 @@ read_poi_group(const int sz, const int tag)
 
        len = gbfgetint32(fin); /* size of group string */
        PP;
-       if (dt->group) xfree(dt->group);
-       dt->group = read_string(&len);
+       if (rdata->group) xfree(rdata->group);
+       rdata->group = read_string(&len);
 #ifdef GPI_DBG
-       printf("Group \"%s\"\n", dt->group);
+       printf("Group \"%s\"\n", rdata->group);
 #endif
        while (gbftell(fin) < (pos + sz)) {
                int subtag = gbfgetint32(fin);
@@ -333,7 +322,7 @@ read_poi_group(const int sz, const int tag)
 static int
 read_tag(const char *caller, const int tag, waypoint *wpt)
 {
-       int pos, sz, len;
+       int pos, sz, len, flag;
        char *str;
 #ifdef GPI_DBG
        int subtag;
@@ -355,19 +344,16 @@ read_tag(const char *caller, const int tag, waypoint *wpt)
                        break;
                        
                case 0x5:       /* group bitmap (BMP: <= 24x24) */
-#ifdef GPI_DBG
-                       // store_bitmap("gpi_bitmap", sz);
-#endif
                        break;
 
                case 0x7:       /* category */
                        (void) gbfgetint32(fin);
                        (void) gbfgetint16(fin);
-                       if (dt->category) xfree(dt->category);
+                       if (rdata->category) xfree(rdata->category);
                        PP;
-                       dt->category = read_string(NULL);
+                       rdata->category = read_string(NULL);
 #ifdef GPI_DBG
-                       printf("Category: \"%s\"\n", dt->category);
+                       printf("Category: \"%s\"\n", rdata->category);
 #endif
                        break;
                        
@@ -381,19 +367,20 @@ read_tag(const char *caller, const int tag, waypoint *wpt)
                        break;
                        
                case 0xe:       /* ? notes ? */
-                       (void) gbfgetc(fin);
-                       if (dt->D2 == 0x1D) {
+                       flag = gbfgetc(fin);
+                       if (flag == 0x01) {
                                len = gbfgetint32(fin);
                                PP;
                                str = read_string(NULL);
                        }
-                       else {
+                       else if (flag == 0x32) {
                                len = gbfgetint16(fin);
                                str = xmalloc(len + 1);
                                str[len] = '\0';
                                PP;
                                gbfread(str, 1, len, fin);
                        }
+                       else break;
 #ifdef GPI_DBG
                        printf("Notes: \"%s\"\n", str);
 #endif
@@ -415,7 +402,6 @@ read_tag(const char *caller, const int tag, waypoint *wpt)
                        break;
                        
                case 0x8000b:   /* address (street/city...) */
-                       gbfseek(fin, sz, SEEK_CUR);
                        /* ToDo */
                        break;
 
@@ -455,14 +441,27 @@ read_tag(const char *caller, const int tag, waypoint *wpt)
 static void
 write_string(const char *str)
 {
-       int len = strlen(str);
+       int len;
        
+       len = strlen(str);
+
+       gbfputint32(len + 4, fout);
        gbfwrite("EN", 1, 2, fout);
        gbfputint16(len, fout);
        gbfwrite(str, 1, len, fout);
 }
 
 
+static int
+compare_wpt_cb(const queue *a, const queue *b)
+{
+       const waypoint *wa = (waypoint *) a;
+       const waypoint *wb = (waypoint *) b;
+       
+       return strcmp(wa->shortname, wb->shortname);
+}
+
+
 static char
 compare_strings(const char *s1, const char *s2)
 {
@@ -475,129 +474,161 @@ compare_strings(const char *s1, const char *s2)
 }
 
 
-static void
-write_header(void)
+static writer_data_t *
+wdata_alloc()
 {
-       struct tm tm;
-       time_t time;
-       
-       tm = *gmtime(&gpsbabel_now);
-       tm.tm_year -= 20;
-       time = mkgmtime(&tm);
-       time += SECONDS_PER_DAY;
-       
-       gbfputint32(0, fout);
-       gbfputint32(0x16, fout);
-       gbfwrite("GRMREC00", 1, 8, fout);
-       gbfputint32(time, fout);
-       gbfputint16(0, fout);
-       gbfputint16(6, fout);
-       gbfwrite("my.gpi", 1, 6, fout);
-       gbfputint32(1, fout);
-       gbfputint32(0xc, fout);
-       gbfwrite("POI", 1, 3, fout);
-       gbfputc(0, fout);
-       gbfputc(0, fout);
-       gbfputc(0, fout);
-       gbfwrite("00", 1, 2, fout);
-       gbfputint32(codepage, fout);
+       writer_data_t *res;
+
+       res = xcalloc(1, sizeof(*res));
+       QUEUE_INIT(&res->Q);
+       waypt_init_bounds(&res->bds);
+
+       return res;
 }
 
+
 static void
-enum_waypt_cb(const waypoint *ref)
+wdata_free(writer_data_t *data)
 {
-       waypoint *wpt;
-       char *str;
        queue *elem, *tmp;
-
-       QUEUE_FOR_EACH(&wptq, elem, tmp) {
-               waypoint *cmp = (waypoint *) elem;
+       
+       QUEUE_FOR_EACH(&data->Q, elem, tmp) {
+               waypoint *wpt = (waypoint *)elem;
                
-               if ((compare_strings(cmp->shortname, ref->shortname) == 0) &&
-                   (cmp->latitude == ref->latitude) &&
-                   (cmp->longitude == ref->longitude) &&
-                   (compare_strings(cmp->description, ref->description) == 0)) return;
+               if (wpt->extra_data) xfree(wpt->extra_data);
+               waypt_free(wpt);
        }
+
+       if (data->top_left) wdata_free(data->top_left);
+       if (data->top_right) wdata_free(data->top_right);
+       if (data->buttom_left) wdata_free(data->buttom_left);
+       if (data->buttom_right) wdata_free(data->buttom_right);
        
-       wpt = waypt_dupe(ref);
+       xfree(data);
+}
 
-       ENQUEUE_TAIL(&wptq, &wpt->Q);
 
-       str = mkshort(short_h, wpt->shortname);
-       xfree(wpt->shortname);
-       wpt->shortname = str;
-       
-       all_points_sz += 12;            /* tag/sz/sub-sz */
-       all_points_sz += 19;            /* poi fixed size */
-       all_points_sz += strlen(wpt->shortname);
-       all_points_sz += 10;            /* tag(4) */
+static void
+wdata_add_wpt(writer_data_t *data, waypoint *wpt)
+{
+       data->ct++;
+       ENQUEUE_TAIL(&data->Q, &wpt->Q);
+       waypt_add_to_bounds(&data->bds, wpt);
+}
 
-       str = NULL;
-       if (opt_descr) {
-               if (wpt->description && *wpt->description)
-                       str = xstrdup(wpt->description);
-       }
-       else if (opt_notes) {
-               if (wpt->notes && *wpt->notes)
-                       str = xstrdup(wpt->notes);
+
+static void
+wdata_check(writer_data_t *data)
+{
+       queue *elem, *tmp;
+       double center_lat, center_lon;
+
+       if (data->ct <= WAYPOINTS_PER_BLOCK) {
+               if (data->ct) sortqueue(&data->Q, compare_wpt_cb);
+               return;
        }
-       else if (opt_pos)
-               str = pretty_deg_format(wpt->latitude, wpt->longitude, 's', 0);
 
-       if (str) {      /* this will be stored into street-address field */
-               all_points_sz += 22 + strlen(str);
-               wpt->extra_data = str;
+       /* compute the center of current bounds */
+       if (data->bds.max_lat == data->bds.min_lat) center_lat = data->bds.max_lat;
+       else center_lat = (data->bds.max_lat + data->bds.min_lat) / 2;
+
+       if (data->bds.max_lon == data->bds.min_lon) center_lon = data->bds.max_lon;
+       else center_lon = (data->bds.max_lon + data->bds.min_lon) / 2;
+       
+       QUEUE_FOR_EACH(&data->Q, elem, tmp) {
+               waypoint *wpt = (waypoint *) elem;
+               writer_data_t **ref;
+
+               if (wpt->latitude < center_lat) {
+                       if (wpt->longitude < center_lon)
+                               ref = &data->buttom_left;
+                       else
+                               ref = &data->buttom_right;
+               } else {
+                       if (wpt->longitude < center_lon)
+                               ref = &data->top_left;
+                       else
+                               ref = &data->top_right;
+               }
+
+               if (*ref == NULL) *ref = wdata_alloc();
+
+               data->ct--;
+               dequeue(&wpt->Q);
+               
+               wdata_add_wpt(*ref, wpt);
        }
        
-       str = wpt->description;
-       if (! str) str = wpt->notes;
-       if (str) all_points_sz += (16 + strlen(str));
-               
-       waypt_add_to_bounds(&bds, wpt);
+       if (data->top_left) wdata_check(data->top_left);
+       if (data->top_right) wdata_check(data->top_right);
+       if (data->buttom_left) wdata_check(data->buttom_left);
+       if (data->buttom_right) wdata_check(data->buttom_right);
 }
 
 
 static int
-compare_wpt_cb(const queue *a, const queue *b)
+wdata_compute_size(writer_data_t *data)
 {
-       const waypoint *wa = (waypoint *) a;
-       const waypoint *wb = (waypoint *) b;
+       queue *elem, *tmp;
+       int res;
        
-       return strcmp(wa->shortname, wb->shortname);
+       res = 23;       /* bounds, ... of tag 0x80008 */
+               
+       QUEUE_FOR_EACH(&data->Q, elem, tmp) {
+               waypoint *wpt = (waypoint *) elem;
+               char *str;
+               
+               res += 12;              /* tag/sz/sub-sz */
+               res += 19;              /* poi fixed size */
+               res += strlen(wpt->shortname);
+               res += 10;              /* tag(4) */
+               
+               str = NULL;
+               if (opt_descr) {
+                       if (wpt->description && *wpt->description)
+                               str = xstrdup(wpt->description);
+               }
+               else if (opt_notes) {
+                       if (wpt->notes && *wpt->notes)
+                               str = xstrdup(wpt->notes);
+               }
+               else if (opt_pos)
+                       str = pretty_deg_format(wpt->latitude, wpt->longitude, 's', 0);
+                       
+               if (str) {      /* this will be stored into street-address field */
+                       res += 22 + strlen(str);
+                       wpt->extra_data = str;
+               }
+               
+               str = wpt->description;
+               if (! str) str = wpt->notes;
+               if (str) res += (16 + strlen(str));
+       }
+
+       if (data->top_left) res += wdata_compute_size(data->top_left);
+       if (data->top_right) res += wdata_compute_size(data->top_right);
+       if (data->buttom_left) res += wdata_compute_size(data->buttom_left);
+       if (data->buttom_right) res += wdata_compute_size(data->buttom_right);
+
+       data->sz = res;
+
+       return res + 12;        /* 12 = caller needs info about tag header size */
 }
 
 
 static void
-write_data(const char *group)
+wdata_write(const writer_data_t *data)
 {
        queue *elem, *tmp;
-       int size;
-       
-       size = all_points_sz;
-       size += 8;
-       size += strlen(group);
-       
-       size += 12 + 23;
-       
-       /* --- group header --- */
-       
-       gbfputint32(0x80009, fout);
-       gbfputint32(size + BMP_SIZE + 8, fout);
-       gbfputint32(size, fout);
-       
-       gbfputint32(strlen(group) + 4, fout);
-       write_string(group);
-       
-       /* --- list header --- */
        
        gbfputint32(0x80008, fout);
-       gbfputint32(all_points_sz + 23, fout);
-       gbfputint32(23, fout);
+       gbfputint32(data->sz, fout);
+       gbfputint32(23, fout);  /* bounds + three bytes */
 
-       gbfputint32(GPS_Math_Deg_To_Semi(bds.max_lat), fout);
-       gbfputint32(GPS_Math_Deg_To_Semi(bds.max_lon), fout);
-       gbfputint32(GPS_Math_Deg_To_Semi(bds.min_lat), fout);
-       gbfputint32(GPS_Math_Deg_To_Semi(bds.min_lon), fout);
+       gbfputint32(GPS_Math_Deg_To_Semi(data->bds.max_lat), fout);
+       gbfputint32(GPS_Math_Deg_To_Semi(data->bds.max_lon), fout);
+       gbfputint32(GPS_Math_Deg_To_Semi(data->bds.min_lat), fout);
+       gbfputint32(GPS_Math_Deg_To_Semi(data->bds.min_lon), fout);
        
        gbfputc(0, fout);               /* three unknown bytes */
        gbfputc(0, fout);               /* ? should be zero ? */
@@ -605,7 +636,7 @@ write_data(const char *group)
        
        gbfputint32(0x1000100, fout);   /* ? const 0x1000100 ? */
        
-       QUEUE_FOR_EACH(&wptq, elem, tmp) {
+       QUEUE_FOR_EACH(&data->Q, elem, tmp) {
                char *str;
                int s0, s1;
                waypoint *wpt = (waypoint *)elem;
@@ -613,7 +644,6 @@ write_data(const char *group)
                str = wpt->description;
                if (! str) str = wpt->notes;
 
-               /* --- poi header --- */
                gbfputint32(0x80002, fout);             
                
                s0 = s1 = 19 + strlen(wpt->shortname);
@@ -630,21 +660,18 @@ write_data(const char *group)
                gbfputint32(GPS_Math_Deg_To_Semi(wpt->longitude), fout);
        
                gbfputint16(1, fout);   /* ? always 1 ? */
-               gbfputc(1, fout);       /* seems to be 1 when extra options present */
-                                       /* because we always write tag(4) this should be okay */
+               gbfputc(0, fout);       /* seems to be 1 when extra options present */
                
-               gbfputint32(strlen(wpt->shortname) + 4, fout);
                write_string(wpt->shortname);
                
                gbfputint32(4, fout);   /* tag(4) */
                gbfputint32(2, fout);
-               if (opt_hide) gbfputint16(1, fout);     /* values != 0 hides the bitmap */
+               if (opt_hide) gbfputint16(0x3ff, fout); /* values != 0 hides the bitmap */
                else gbfputint16(0, fout);
                
                if (str) {
                        gbfputint32(0xa, fout);
-                       gbfputint32(strlen(str) + 8, fout);
-                       gbfputint32(strlen(str) + 4, fout);
+                       gbfputint32(strlen(str) + 8, fout);     /* string + string header */
                        write_string(str);
                }
                
@@ -652,25 +679,100 @@ write_data(const char *group)
                if (str) {
                        gbfputint32(0x8000b, fout);
                        gbfputint32(strlen(str) + 10, fout);
-                       gbfputint32(0x2, fout);
+                       gbfputint32(0x2, fout);         /* ? always 2 ? */
                        gbfputint16(0x10, fout);        /* 0x10 = StreetAddress */
-                       gbfputint32(strlen(str) + 4, fout);
                        write_string(str);
-                       xfree(str);
                }
        }
        
-       if (BMP_SIZE) {
+       if (data->top_left) wdata_write(data->top_left);
+       if (data->top_right) wdata_write(data->top_right);
+       if (data->buttom_left) wdata_write(data->buttom_left);
+       if (data->buttom_right) wdata_write(data->buttom_right);
+}
+
+
+static void
+write_category(const char *category)
+{
+       int sz;
+       
+       sz = wdata_compute_size(wdata);
+       sz += 8;        /* string header */
+       sz += strlen(opt_cat);
+       
+       gbfputint32(0x80009, fout);
+       if ((! opt_hide) && BMP_SIZE)
+               gbfputint32(sz + BMP_SIZE + 8, fout);
+       else
+               gbfputint32(sz, fout);
+       gbfputint32(sz, fout);
+       
+       write_string(opt_cat);
+       
+       wdata_write(wdata);
+       if ((! opt_hide) && BMP_SIZE) {
                gbfputint32(5, fout);   /* simple bitmap / GPSBabel logo */
                gbfputint32(BMP_SIZE, fout);
                gbfwrite(gpi_bitmap, 1, BMP_SIZE, fout);
        }
+}
 
-       gbfputc(-1, fout);              /* end of group */
-       gbfputint32(0xff, fout);        /* final tag */
 
+static void
+write_header(void)
+{
+       struct tm tm;
+       time_t time;
+       
+       tm = *gmtime(&gpsbabel_now);
+       tm.tm_year -= 20;
+       time = mkgmtime(&tm);
+       time += SECONDS_PER_DAY;
+       
+       gbfputint32(0, fout);
+       gbfputint32(0x16, fout);
+       gbfwrite("GRMREC00", 1, 8, fout);
+       gbfputint32(time, fout);
        gbfputint16(0, fout);
+       gbfputint16(6, fout);
+       gbfwrite("my.gpi", 1, 6, fout);
+       gbfputint32(1, fout);
+       gbfputint32(0xc, fout);
+       gbfwrite("POI", 1, 3, fout);
        gbfputc(0, fout);
+       gbfputc(0, fout);
+       gbfputc(0, fout);
+       gbfwrite("00", 1, 2, fout);
+       gbfputint32(codepage, fout);
+}
+
+
+static void
+enum_waypt_cb(const waypoint *ref)
+{
+       waypoint *wpt;
+       char *str;
+       queue *elem, *tmp;
+
+       QUEUE_FOR_EACH(&wdata->Q, elem, tmp) {
+               waypoint *cmp = (waypoint *) elem;
+               
+               /* sort out nearly equal waypoints */
+               if ((compare_strings(cmp->shortname, ref->shortname) == 0) &&
+                   (cmp->latitude == ref->latitude) &&
+                   (cmp->longitude == ref->longitude) &&
+                   (compare_strings(cmp->description, ref->description) == 0) &&
+                   (compare_strings(cmp->notes, ref->notes) == 0)) return;
+       }
+       
+       wpt = waypt_dupe(ref);
+
+       str = mkshort(short_h, wpt->shortname);
+       xfree(wpt->shortname);
+       wpt->shortname = str;
+
+       wdata_add_wpt(wdata, wpt);
 }
 
 
@@ -684,7 +786,7 @@ garmin_gpi_rd_init(const char *fname)
        char cp[8];
        
        fin = gbfopen_le(fname, "rb", MYNAME);
-       dt = xcalloc(1, sizeof(*dt));
+       rdata = xcalloc(1, sizeof(*rdata));
 
        read_header();
        
@@ -705,10 +807,6 @@ garmin_gpi_wr_init(const char *fname)
        
        fout = gbfopen_le(fname, "wb", MYNAME);
        
-       QUEUE_INIT(&wptq);
-       all_points_sz = 0;
-       waypt_init_bounds(&bds);
-
        short_h = mkshort_new_handle();
        
        setshort_length(short_h, 1024);
@@ -729,20 +827,22 @@ garmin_gpi_wr_init(const char *fname)
                        break;
                }
        }
-       
+
        if (! codepage) {
                warning(MYNAME ": Unsupported character set (%s)!\n", global_opts.charset_name);
                fatal(MYNAME ": Valid values are CP1250 to CP1257.\n");
        }
+
+       wdata = wdata_alloc();
 }
 
 
 static void 
 garmin_gpi_rd_deinit(void)
 {
-       if (dt->category) xfree(dt->category);
-       if (dt->group) xfree(dt->group);
-       xfree(dt);
+       if (rdata->category) xfree(rdata->category);
+       if (rdata->group) xfree(rdata->group);
+       xfree(rdata);
        gbfclose(fin);
 }
 
@@ -750,13 +850,8 @@ garmin_gpi_rd_deinit(void)
 static void 
 garmin_gpi_wr_deinit(void)
 {
-       queue *elem, *tmp;
-       
+       wdata_free(wdata);
        mkshort_del_handle(&short_h);
-       QUEUE_FOR_EACH(&wptq, elem, tmp) {
-               dequeue(elem);
-               waypt_free((waypoint *) elem);
-       }
        gbfclose(fout);
 }
 
@@ -764,11 +859,9 @@ garmin_gpi_wr_deinit(void)
 static void
 garmin_gpi_read(void)
 {
-       while (! gbfeof(fin)) { /* main loop */
-               int tag;
-               
-               tag = gbfgetint32(fin);
-               if ((tag == 0xffff) || (tag == 0xff)) return;
+       while (1) {
+               int tag = gbfgetint32(fin);
+               if (tag == 0xffff) return;
                if (! read_tag("garmin_gpi_read", tag, NULL)) return;
        };
 }
@@ -779,11 +872,12 @@ garmin_gpi_write(void)
 {
        if (strlen(opt_cat) == 0) fatal(MYNAME ": Can't write empty category!\n");
        
-       write_header();
        waypt_disp_all(enum_waypt_cb);
-       sortqueue(&wptq, compare_wpt_cb);
-
-       write_data(opt_cat);
+       wdata_check(wdata);
+       write_header();
+       write_category(opt_cat);
+       gbfputint32(0xffff, fout);      /* final tag */
+       gbfputint32(0, fout);
 }
 
 /**************************************************************************/